package gitobject;



import repository.Repository;
import sha1.SHA1;

import zlib.ZLibUtils;

import java.io.*;
import java.util.*;


public class Tree extends GitObject{
    protected ArrayList<GitObject> treeList;	//GitObjects in tree


    /**
     * Constructor
     * @param dir 根目录起的相对路径
     * @param treeMap treekey与相对路径的键值对
     * @param currentIndexList 所有被add过的文件
     * @throws Exception
     */
    public Tree(String dir, ArrayList<String> currentIndexList, Map<String, String> treeMap) throws Exception {
        ArrayList<String> blobInDir = new ArrayList<String>();
        Set<String> subdirSet = new HashSet<String>();
        for (String blob:currentIndexList) {
            String[] blobInfo = blob.split(" ");
            // contains current dir
            if (blobInfo[1].indexOf(dir)==0) { // 父路径与dir匹配
                // only top level dir or file
                String cutPath = blobInfo[1].substring(dir.length()+1); // 切割掉当前目录
                if (!cutPath.contains(File.separator)) // 切割后不含分隔符顶层文件
                    blobInDir.add(blob);
                else { // 含分隔符则说明文件子文件夹中，取子文件夹路径即可
                    String dirPath = blobInfo[1].substring(0, dir.length()+cutPath.indexOf(File.separator)+1);
                    subdirSet.add(dirPath);
                }

            }
        }
        blobInDir.sort(Comparator.naturalOrder());
        ArrayList<String> subdirInDir = new ArrayList<>(subdirSet);
        subdirInDir.sort(Comparator.naturalOrder());
        this.value = "";
        for (String subdir:subdirInDir) {
            String treekey = treeMap.get(subdir);
            this.value += "040000 tree " + treekey + " " + subdir + "\n";
        }

        for (String blob:blobInDir) {
            String[] blobInfo = blob.split(" ");
            this.value += "100644 blob " + blobInfo[0] + " " + blobInfo[1] + "\n";
        }

        this.fmt = "tree";
        this.mode = "040000";
        this.key = SHA1.getHash(this.value);
//        System.out.println(this.value);
        this.compressWrite();
    }

    /**
     * Constructor for commit tree
     * @param currentIndexList 所有被add过的文件
     * @param treeMap treekey与相对路径的键值对
     * @throws Exception
     */
    public Tree(ArrayList<String> currentIndexList, Map<String, String> treeMap) throws Exception {
        this.value = "";
        for (Map.Entry<String, String> entry : treeMap.entrySet()) {
            String path = entry.getKey();
            if (!path.contains(File.separator)) {
                this.value += "040000 tree " + entry.getValue() + " " + path + "\n";
            }
        }
        currentIndexList.sort(Comparator.naturalOrder());
        for (String blob:currentIndexList) {
            if (!blob.contains(File.separator)) {
                String[] blobInfo = blob.split(" ");
                if (new File(Repository.getWorkTree()+File.separator+blobInfo[1]).exists()) {
                    this.value += "100644 blob " + blobInfo[0] + " " + blobInfo[1] + "\n";
                }
            }
        }

        this.fmt = "tree";
        this.mode = "040000";
        this.key = SHA1.getHash(this.value);
//        System.out.println(this.value);
        this.compressWrite();
    }

    public Tree(){}
    
    /**
     * Constructor
     * @param file
     * @throws Exception
     */
    public Tree(File file) throws Exception {
        this.fmt = "tree";
        this.mode = "040000";
        this.value = "";
        this.key = genKey(file);
//        writeObject();
//        compressWrite();
//        System.out.println("creating a tree");
    }

    /**
     * Deserialize a tree object with treeId and its path.
     * @param Id
     * @throws IOException
     */
    public static Tree deserialize(String Id) throws IOException {
        Tree tree = new Tree();
        try {
            // for writeObject() method
//            String curPath = "worktree"+ File.separator +".jit"+ File.separator +"objects" +
//                    File.separator + Id.substring(0, 2) + File.separator + Id.substring(2);
//            File f = new File(curPath)
//          //反序列化
//            ObjectInputStream in = new ObjectInputStream(new FileInputStream(f));
//            tree = (Tree) in.readObject();
            // for compressWrite() method
            String curPath = path + File.separator + Id.substring(0, 2) + File.separator + Id.substring(2);
            File f = new File(curPath);
            FileInputStream fis = new FileInputStream(f);
            // 解压缩文件输入流
            byte[] compressedData = ZLibUtils.decompress(fis);
            // 执行解压缩
            byte[] data = ZLibUtils.decompress(compressedData);
            // 反序列化
            ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(data));
            tree = (Tree) in.readObject();
            in.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

//        System.out.println(tree.value);
        return tree;
    }


    /**
     * Sort the files in a certain order. You should consider whether it's a file or a directory.
     * @param fs
     * @return List
     */
    public List sortFile(File[] fs){
        List fileList = Arrays.asList(fs);
        Collections.sort(fileList, new Comparator<File>() {
            @Override //表示重写方法
            public int compare(File o1, File o2) {
                /* Add your code here. -- DONE --TESTED*/
                // fs也被修改为排好序的文件列表
                // 文件夹优先
                if (o1.isDirectory() && !o2.isDirectory()) return -1;
                if (!o1.isDirectory() && o2.isDirectory()) return 1;
                return o1.getName().compareTo(o2.getName()); // 字母序（参数如果靠前则返回正数）
            }
        });
        return fileList;
    }

    /**
     * Generate the key of a tree object. List **all** files in dir.
     * @param dir
     * @return String
     * @throws Exception
     */
    public String genKey(File dir) throws Exception{
        File[] fs = dir.listFiles();
        List op = sortFile(fs);

        for (int i=0; i<fs.length; i++) {
            if (fs[i].isDirectory()) {
                Tree tree = new Tree(fs[i]);
//                treeList.add(tree);
                String workDir = Repository.getWorkTree();
                String fileName = fs[i].getPath().substring(workDir.length() + 1);
                value += tree.toString() + " " + fileName + "\n";
            }
            else if (fs[i].isFile()) {
                Blob blob = new Blob(fs[i]);
//                treeList.add(blob);
                String workDir = Repository.getWorkTree();
                String fileName = fs[i].getPath().substring(workDir.length() + 1);
                value += blob.toString() + " " + fileName + "\n";
            }
        }
//        System.out.println(value);
        String key = SHA1.getHash(value);
        return key;
    }

    @Override
    public String toString(){
        return "040000 tree " + key;
    }

}
